#include "Analyzer.h"
#include <list>
#include <istream>
#include <ostream>
#include <sstream>
#include <iomanip>

using namespace std;

void Analyze::initFields(){
	string pred[] = { "char", "short", "int", "long", "float", "double", "void"};
	string ifElseTemp[] = {"if", "else"};
	string key1[] = {"for", "while", "function"};
	string key2[] = { "class", "private", "const", "public", "virtual"};
	string oper[] = { "++",  "--",  "==",  "->" ,  "=",  "+",  "-",  "*",  "&",  "<<",  ">>"};
	string bracketsCurlyTemp[] = {"{", "}"};
	string bracketsSquareTemp[] = {"[", "]"};
	string bracketsCircleTemp[] = {"(", ")"};
	string main[] = {"MAIN", "main"};

	predefinedType = new list<const string>(pred, pred+7);
	ifElse =		 new list<const string>(ifElseTemp, ifElseTemp+2);
	keywordsGroup1 = new list<const string>(key1, key1+3);
	keywordsGroup2 = new list<const string>(key2, key2+5);
	operands =		 new list<const string>(oper, oper+11);
	bracketsCurly =	 new list<const string>(bracketsCurlyTemp, bracketsCurlyTemp+2);
	bracketsSquare = new list<const string>(bracketsSquareTemp, bracketsSquareTemp+2);
	bracketsCircle =  new list<const string>(bracketsCircleTemp, bracketsCircleTemp+2);
	mainLine =		 new list<const string>(main, main+2);
	
	arr[PREDEFINED] = predefinedType;
	arr[IFELSE] = ifElse;
	arr[KEYWORDS1] = keywordsGroup1;
	arr[KEYWORDS2] = keywordsGroup2;
	arr[OPERATORS] = operands;
	arr[BRACKETSCURLY] = bracketsCurly;
	arr[BRACKETSSQUARE] = bracketsSquare;
	arr[BRACKETSCIRCLE] = bracketsCircle;
	arr[MAIN] = mainLine;
}

void Analyze::destroyFields(){
	delete predefinedType;
	delete ifElse;
	delete keywordsGroup1;
	delete keywordsGroup2;
	delete operands;
	delete bracketsCircle;
	delete bracketsCurly;
	delete bracketsSquare;
}

Analyze::Analyze() :
	nofbracketsCircle(0),
	nofbracketsCurly(0),
	nofbracketsSquare(0),
	nof_if(0),
	lastTokenType(NONE),
	lastToken(NULL),
	nofLine(0),
	mainFlag(0)
{
	initFields();
};
Analyze::~Analyze() {
	destroyFields(); 

	for (list<const string*>::const_iterator iter = (this->errors).begin(); iter!=this->errors.end(); iter++)
	{
		delete *iter;
	}

	for (list<const string*>::const_iterator iter = (this->varsAndFuncsLst).begin(); iter!=this->varsAndFuncsLst.end(); iter++)
	{
		delete *iter;
	}

};

TokenTypes Analyze::getTokenType(const string& token) const{
	int type;
	for(type = PREDEFINED; type<NONE; type++)
	{
		for(list<const string>::const_iterator it = arr[type]->begin(); it != arr[type]->end(); it++)
		{
			if (token.compare(*it)==0) return (TokenTypes) type;
		}
	}
	return NONE;
};

void Analyze::compileLine(const list<const string *>& line) 
{
	bool lineOK = true;
	nofLine++;
	TokenTypes tokenType;
	lastTokenType = NONE;
	lastToken = NULL;
	for(list<const string *>::const_iterator iter = line.begin(); iter!=line.end(); iter++)
	{
		tokenType = getTokenType(**iter);
		switch(tokenType)
		{
			case PREDEFINED:
				// Check if lastToken != PREDEFINED
				if (lastTokenType == PREDEFINED){
					ostringstream oss (ostringstream::out);
					oss << "Line " << nofLine << ":Error - Multiple type definition" << endl;
					string *err = new string(oss.str());
					errors.push_back(err);
					lineOK = false;
				}


				break;
			case IFELSE:
				/*update fields*/
				if ((**iter).compare("if") == 0) {
					nof_if++;
				} else /*equal to else*/ {
					nof_if--;
				}

				/*check status*/
				if (nof_if < 0 /*else withou if*/) {
					ostringstream oss (ostringstream::out);
					oss << "Line " << nofLine << ":Error - \"else\" without \"if\" " << endl;
					string *err = new string(oss.str());
					errors.push_back(err);
					lineOK = false;
					// Disabling trailing error
					nof_if = 0;
				}

				break;
			case KEYWORDS1:
			case KEYWORDS2:
			case OPERATORS:
				break;
			case BRACKETSCURLY:
				/*update fields*/
				if ((**iter).compare("{") == 0) {
					nofbracketsCurly++;
				} else /*equal to "}"*/ {
					nofbracketsCurly--;
				}

				/*check status*/
				if (nofbracketsCurly < 0 /*if withou else*/) {
					ostringstream oss (ostringstream::out);
					oss << "Line " << nofLine << ":Error - \"}\" without \"{\" " << endl;
					string *err = new string(oss.str());
					errors.push_back(err);
					lineOK = false;
					// Disabling trailing error
					nofbracketsCurly = 0;
				}
				break;
			case BRACKETSSQUARE:
				/*update fields*/
				if ((**iter).compare("[") == 0) {
					nofbracketsSquare++;
				} else /*equal to "]"*/ {
					nofbracketsSquare--;
				}

				/*check status*/
				if (nofbracketsSquare < 0 /*if withou else*/) {
					ostringstream oss (ostringstream::out);
					oss << "Line " << nofLine << ":Error - \"]\" without \"[\" " << endl;
					string *err = new string(oss.str());
					errors.push_back(err);
					lineOK = false;
					// Disabling trailing error
					nofbracketsSquare = 0;
				}
				break;
			case BRACKETSCIRCLE:
				/*update fields*/
				if ((**iter).compare("(") == 0) {
					nofbracketsCircle++;
				} else /*equal to ")"*/ {
					nofbracketsCircle--;
				}

				/*check status*/
				if (nofbracketsCircle < 0 /*if without else*/) {
					ostringstream oss (ostringstream::out);
					oss << "Line " << nofLine << ":Error - \")\" without \"(\" " << endl;
					string *err = new string(oss.str());
					errors.push_back(err);
					lineOK = false;
					// Disabling trailing error
					nofbracketsCircle = 0;
				}
				break;

			case MAIN:
				if (mainFlag != 0){
					ostringstream oss (ostringstream::out);
					oss << "Line " << nofLine << ":Error - Variable \'" << **iter << "\' already declared" << endl;
					string *err = new string(oss.str());
					errors.push_back(err);
					lineOK = false;
					break;
				} 
				if (lastToken != NULL){
					if (lastToken->compare("function") == 0){

						mainFlag = 1;
						varsAndFuncsMap.insert(pair<string, string>(**iter, "function"));
						ostringstream oss (ostringstream::out);
						oss << left << setw(20) << *lastToken << "\t" << **iter << endl;
						string *def = new string(oss.str());
						varsAndFuncsLst.push_back(def);
					}
				}
				break;

			case NONE:
				/*check if number*/
				istringstream iss (**iter);
				double test = 0.0;
				if (iss >> test) {
					/*is number*/
					break;
				} 
				
				if (lastTokenType == PREDEFINED || (lastToken != NULL && (lastToken->compare("function") == 0))) {
					// Defining a new var or function
					// Search if it already exists
					map<const string, const string>::iterator mapIter = varsAndFuncsMap.find(**iter);
					if (mapIter != varsAndFuncsMap.end()){
						// Error - already exist
						ostringstream oss (ostringstream::out);
						oss << "Line " << nofLine << ":Error - Variable \'" << **iter << "\' already declared" << endl;
						string *err = new string(oss.str());
						errors.push_back(err);
						lineOK = false;
						break;
					}
					// if not, check if starts with letter and insert it
					char c = (*iter)->at(0);
					if (!((c<='z' && c>='a') || (c<='Z' && c>='A'))){
						ostringstream oss (ostringstream::out);
						oss << "Line " << nofLine << ":Error - Identifier has to start with an alphabetic character" << endl;
						string *err = new string(oss.str());
						errors.push_back(err);
						lineOK = false;

					}
					if (lineOK) {
						varsAndFuncsMap.insert(pair<string, string>(**iter, *lastToken));
						ostringstream oss (ostringstream::out);
						oss << left << setw(20) << *lastToken << "\t" << **iter << endl;
						string *def = new string(oss.str());
						varsAndFuncsLst.push_back(def);
					}
				} else {
					map<const string, const string>::iterator mapIter = varsAndFuncsMap.find(**iter);
					if (mapIter == varsAndFuncsMap.end()){
						// Error - not declared
						ostringstream oss (ostringstream::out);
						oss << "Line " << nofLine << ":Error - Variable \'" << **iter << "\' is not declared" << endl;
						string *err = new string(oss.str());
						errors.push_back(err);
						lineOK = false;
					}
				}
				break;
		} // Finished Switch

		// Check if lastToken != PREDEFINED
		if ((lastTokenType == PREDEFINED) && (tokenType != NONE) && (tokenType != PREDEFINED)){
			ostringstream oss (ostringstream::out);
			oss << "Line " << nofLine << ":Error - Identifier expected" << endl;
			string *err = new string(oss.str());
			errors.push_back(err);
		}
		lastTokenType = tokenType;
		lastToken = *iter;
		
	}
	if (mainFlag != 1){
		ostringstream oss (ostringstream::out);
		oss << "Line 1:Error - Should contain Main function" << endl;
		string *err = new string(oss.str());
		errors.push_back(err);
		mainFlag = 1;
	}

}
const list<const string *>& Analyze::getErrors() const {return errors;};
const list<const string *>& Analyze::getVarsAndFuncs() const {return varsAndFuncsLst;};

void Analyze::finishCompile(){
	if (nofbracketsCircle!=0){
		ostringstream oss (ostringstream::out);
		oss << "Line " << nofLine << ":Error - Expecting \")\"" << endl;
		string *err = new string(oss.str());
		errors.push_back(err);
	}
	if (nofbracketsCurly!=0){
		ostringstream oss (ostringstream::out);
		oss << "Line " << nofLine << ":Error - Expecting \"}\"" << endl;
		string *err = new string(oss.str());
		errors.push_back(err);
	}
	if (nofbracketsSquare!=0){
		ostringstream oss (ostringstream::out);
		oss << "Line " << nofLine << ":Error - Expecting \"]\"" << endl;
		string *err = new string(oss.str());
		errors.push_back(err);
	}

}
